
import { LayersAction, LayersActionTypes, LayerSourceLoaded } from '../actions/layers.action';
import { Layer } from "../../class";
import { LayerTypes } from '../../enums';

export const initialLayer: Layer = {
    id: null,
    title: null,
    type: null,
    options: null,
    style: null,
    alias: null,
    mapViewable: true,
    tableViewable: true,
    selectable: true,
    visiable: true,
    disabled: false,
    zIndex: 100,
    opacity: 1,
    tableColumn: null,
    idFieldName: 'id',
    queryable: false,
    queryResult: null,
    visiableTable: false,
    loading: false,
    loaded: false,
    loadError: null,
    data: null,
    default: false,
    fieldTypes: null
};

export interface LayersState {
    entities: { [key: string]: Layer },
    initial: { [key: string]: Layer },
    initialed: boolean,
    initialing: boolean,
    initialError: any,
    lastAdded: Layer[],
    lastRemoved: Layer[],
    layerLoading: boolean,
    layerLoaded: boolean,
    layerLoadError: any,
    layerNextZIndex: number,
    dataNextZindex: number,
    spatialQueryUseable: boolean,
    tables: string[],
    lastAddedTable: string,
    lastRemovedTable: string[],
    tableState: 'visible'|'hidden'|'removed',
    featureSelected: { layerId: string, featureId: string },
    featureDeselected: { layerId: string, featureId: string },
}

export const initialLayerState: LayersState = {
    entities: {},
    initial: {},
    initialed: false,
    initialError: null,
    initialing: false,
    lastAdded: null,
    lastRemoved: null,
    layerLoaded: false,
    layerLoadError: null,
    layerLoading: false,
    layerNextZIndex: 100,
    dataNextZindex: 1,
    spatialQueryUseable: false,
    tables: [],
    lastAddedTable: null,
    lastRemovedTable: null,
    tableState: 'removed',
    featureDeselected: null,
    featureSelected: null
}

export function layersRedecuer(
    state: LayersState = initialLayerState,
    action: LayersAction
): LayersState {
    switch(action.type) {
        case LayersActionTypes.INITIAL_LAYERS: {
            return {
                ...state,
                initial: {},
                initialed: false,
                initialError: null,
                initialing: true
            }
        }
        case LayersActionTypes.INITIAL_LAYERS_FAIL: {
            return {
                ...state,
                initialed: false,
                initialError: action.payload,
                initialing: false
            }
        }
        case LayersActionTypes.INITIAL_LAYERS_SUCCESS: {
            let layerNextZIndex: number = state.layerNextZIndex;
            let dataNextZindex: number = state.dataNextZindex;
            const initial: { [key: string]: Layer } = action.payload.reduce((previousValue, currentValue: Layer) => {
                let zIndex = 0;
                if(currentValue.type === LayerTypes.JSON) {
                    zIndex = dataNextZindex;
                    dataNextZindex++;
                } else {
                    zIndex = layerNextZIndex;
                    layerNextZIndex+=5;
                }
                const layer: Layer = { ...initialLayer, ...currentValue, zIndex };
                previousValue = { ...previousValue, [layer.id]: layer};
                return previousValue;
            }, {});
            return {
                ...state,
                entities: initial,
                initial,
                initialed: true,
                initialError: null,
                initialing: false,
                layerNextZIndex,
                dataNextZindex
            }
        }
        case LayersActionTypes.LOAD_LAYER: {
            return {
                ...state,
                layerLoaded: false,
                layerLoadError: null,
                layerLoading: true
            }
        }
        case LayersActionTypes.LOAD_LAYER_FAIL: {
            return {
                ...state,
                layerLoaded: false,
                layerLoadError: action.payload,
                layerLoading: false
            }
        }
        case LayersActionTypes.LOAD_LAYER_SUCCESS: {
            const payload = action.payload;
            let layerNextZIndex = state.layerNextZIndex;
            let dataNextZindex = state.dataNextZindex;
            let entities = state.entities;            
            let layers = payload.reduce((_layers, _layer) => {
                let layer;
                if(!entities[_layer.id]) {
                    if(_layer.type === LayerTypes.JSON) {
                        layer = { ...initialLayer, ..._layer, zIndex: dataNextZindex };
                        _layers = [ ..._layers, layer];
                        dataNextZindex++;
                    } else {
                        layer = { ...initialLayer, ..._layer, zIndex: layerNextZIndex };
                        _layers = [ ..._layers, layer];
                        layerNextZIndex+=5;
                    }
                    entities = { ...entities, [layer.id]: layer };
                }
                return _layers;
            }, []);
            return {
                ...state,
                entities,
                lastAdded: layers,
                layerLoaded: true,
                layerLoadError: null,
                layerLoading: false,
                layerNextZIndex,
                dataNextZindex
            }
        }
        case LayersActionTypes.REMOVE_LAYER: {
            const id = action.payload;
            const { [id]: removed, ...entities } = state.entities;
            const tables = state.tables.filter(table => table !== id);
            const tableState = tables.length > 0 ? state.tableState : 'removed';
            return {
                ...state,
                entities,
                tables,
                tableState,
                lastRemoved: [ removed ],
                lastRemovedTable: [ id ]
            }
        }
        case LayersActionTypes.LAYERS_SORT: {
            const payload = action.payload;
            let layerNextZIndex = 100;
            const entities = payload.reduce((data, key, index) => {
                const layer = { ...state.entities[key], zIndex: layerNextZIndex };
                layerNextZIndex+=5;
                return { ...data, [key]: layer };
            }, { ...state.entities });
            return {
                ...state,
                entities,
                layerNextZIndex
            };
        }
        case LayersActionTypes.LAYERS_CLEAN: {
            const ids = Object.keys(state.entities);
            const lastRemoved = ids.map(key => state.entities[key]);
            const tableState = 'removed';
            return {
                ...state,
                entities: {},
                layerNextZIndex: 100,
                dataNextZindex: 1,
                lastRemoved,
                lastRemovedTable: ids,
                tableState,
                tables: []
            };
        }
        case LayersActionTypes.LAYER_INVISIBLE: {
            const id = action.payload;
            const layer: Layer = { ...state.entities[id], visiable: false };
            const entities = { ...state.entities, [id]: layer };
            return {
                ...state,
                entities
            };
        }
        case LayersActionTypes.LAYER_VISIBLE: {
            const id = action.payload;
            const layer: Layer = { ...state.entities[id], visiable: true };
            const entities = { ...state.entities, [id]: layer };
            return {
                ...state,
                entities
            };
        }
        case LayersActionTypes.LAYER_OPACITY: {
            const payload = action.payload;
            const id = payload.id;
            const opacity = payload.opacity;
            const layer: Layer = { ...state.entities[id], opacity };
            const entities = { ...state.entities, [id]: layer };
            return {
                ...state,
                entities
            };
        }
        case LayersActionTypes.LAYERS_SPATIAL_QUERY_USEABLE: {
            return {
                ...state,
                spatialQueryUseable: true
            };
        }
        case LayersActionTypes.LAYERS_SPATIAL_QUERY_UnuSEABLE: {
            return {
                ...state,
                spatialQueryUseable: false
            };
        }
        case LayersActionTypes.LAYER_ADD_TABLE: {
            let tables = state.tables;
            if(state.entities[action.payload] && !tables.includes(action.payload)) {
                tables = [...tables, action.payload];
            }
            return {
                ...state,
                tables,
                lastAddedTable: action.payload,
                tableState: 'visible'
            }
        }
        case LayersActionTypes.LAYER_REMOVE_TABLE: {
            const tables = state.tables.filter(id => id !== action.payload);
            const tableState = tables.length === 0 ? 'removed' : state.tableState;
            return {
                ...state,
                tables,
                lastRemovedTable: [ action.payload ],
                tableState
            };
        }
        case LayersActionTypes.LAYER_TABLE_HIDDEN: {
            return {
                ...state,
                tableState: 'hidden'
            }
        }
        case LayersActionTypes.LAYER_TABLE_REMOVED: {
            return {
                ...state,
                tableState: 'removed'
            }
        }
        case LayersActionTypes.LAYER_TABLE_VISIBLE: {
            return {
                ...state,
                tableState: 'visible'
            }
        }
        case LayersActionTypes.LAYER_SOURCE_LOADED: {
            const payload = action.payload;
            const id = payload.id;
            const data = payload.data;
            let entities = state.entities;
            let layer: Layer = entities[id];
            if(layer) {
                layer = { ...layer, data, loaded: true, loadError: null, loading: false, disabled: false };
                entities = { ...entities, [id]: layer };
            }
            return {
                ...state,
                entities
            };
        }
        case LayersActionTypes.LAYER_SOURCE_LOADERROR: {
            const payload = action.payload;
            const id = payload.id;
            const loadError = payload.error;
            let entities = state.entities;
            let layer: Layer = entities[id];
            if(layer) {
                layer = { ...layer, loadError, loaded: false, data: null, loading: false, disabled: true };
                entities = { ...entities, [id]: layer };
            }
            return {
                ...state,
                entities
            };
        }
        case LayersActionTypes.LAYER_SOURCE_LOADING: {
            const payload = action.payload;
            const id = payload;
            let entities = state.entities;
            let layer: Layer = entities[id];
            if(layer) {
                layer = { ...layer, loadError: null, loaded: false, data: null, loading: true, disabled: true };
                entities = { ...entities, [id]: layer };
            }
            return {
                ...state,
                entities
            };
        }
        case LayersActionTypes.LAYER_FEATURE_DESELECTED: {
            return {
                ...state,
                featureDeselected: action.payload
            };
        }
        case LayersActionTypes.LAYER_FEATURE_SELECTED: {
            return {
                ...state,
                featureSelected: action.payload
            };
        }
        case LayersActionTypes.LAYER_ADD_QUERY_RESULT: {
            const payload = action.payload;
            const id = payload.id;
            const result = payload.result;
            let entities = state.entities;
            if(entities[id]) {
                const layer: Layer = { ...entities[id], queryResult: result };
                entities = { ...entities, [layer.id]: layer };
            }
            return {
                ...state,
                entities
            };
        }
        case LayersActionTypes.LAYER_REMOVE_QUERY_RESULT: {
            const id = action.payload;
            let entities = state.entities;
            if(entities[id]) {
                const layer: Layer = { ...entities[id], queryResult: null };
                entities = { ...entities, [layer.id]: layer };
            }
            return {
                ...state,
                entities
            };
        }
        case LayersActionTypes.LAYER_FIELD_TYPES: {
            const payload = action.payload;
            const id = payload.id;
            const fieldTypes = payload.fieldTypes;
            let entities = state.entities;
            if(entities[id]) {
                const layer: Layer = { ...entities[id], fieldTypes };
                entities = { ...entities, [layer.id]: layer };
            }
            return {
                ...state,
                entities
            };
        }
        default: {
            return state;
        }
    }
}